#include "glWidget.h"
#include <QKeyEvent>
#include <QDebug>
#include <cmath>

double PI(4*std::atan(1));

GLWidget::GLWidget(QWidget* parent, unsigned int fps)
  : QGLWidget(parent),
    mFramesPainted(0),
    mXRot(45),
    mYRot(45)
{
  connect(&mTimer, SIGNAL(timeout()), this, SLOT(updateGL()));
  mTimer.start(1000/fps);
  mTime.start();
  setFocusPolicy(Qt::StrongFocus);

  qDebug() << "GLWidget started";
}

GLWidget::~GLWidget()
{
  mTimer.stop();
}

void GLWidget::initializeGL()
{
  loadGLTexture("testimage.png", mTextures);
  glEnable(GL_TEXTURE_2D); //enable 2D texture mapping
  glBindTexture(GL_TEXTURE_2D, mTextures[0]);

  glShadeModel(GL_SMOOTH); //smooth shading
  glClearColor(0, 0, 0, 0); //black background

  //depth buffer setup
  glClearDepth(1);
  glDisable(GL_DEPTH_TEST);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE);

  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //best perspective correction

  glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);

  for (int i(0); i < MAX_PARTICLES; ++i)
  {
    mParticles[i].life = 1;
    mParticles[i].fadeSpeed = rand()%100/1000. + 0.003;
    mParticles[i].r = Color[i*12/MAX_PARTICLES][0];
    mParticles[i].g = Color[i*12/MAX_PARTICLES][1];
    mParticles[i].b = Color[i*12/MAX_PARTICLES][2];
    mParticles[i].xdot = (rand()%50 - 26.0)*10;
    mParticles[i].ydot = (rand()%50 - 26.0)*10;
    mParticles[i].zdot = (rand()%50 - 26.0)*10;
    mParticles[i].xg = 0;
    mParticles[i].yg = -0.8;
    mParticles[i].zg = 0;
  }
}

void GLWidget::resizeGL(int w, int h)
{
  glViewport(0, 0, w, h); //reset viewport

  //setup perspective view
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45, w*1./h, 0.1, 200);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void GLWidget::paintGL()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear screen and depth buffer
  glLoadIdentity(); //reset view

  glRotatef(mXRot, 1, 0, 0);
  glRotatef(mYRot, 0, 1, 0);

  for(int i(0); i < MAX_PARTICLES; ++i)
  {
    double x(mParticles[i].x);
    double y(mParticles[i].y);
    double z(mParticles[i].z - 40);

    glColor4f(mParticles[i].r, mParticles[i].g, mParticles[i].b, mParticles[i].life);
    glBegin(GL_TRIANGLE_STRIP);
      glTexCoord2d(1, 1); glVertex3f(x+0.5, y+0.5, z);
      glTexCoord2d(0, 1); glVertex3f(x-0.5, y+0.5, z);
      glTexCoord2d(1, 0); glVertex3f(x+0.5, y-0.5, z);
      glTexCoord2d(0, 0); glVertex3f(x-0.5, y-0.5, z);
    glEnd();

    double slowdown(2);
    mParticles[i].x += mParticles[i].xdot/(slowdown*1000);
    mParticles[i].y += mParticles[i].ydot/(slowdown*1000);
    mParticles[i].z += mParticles[i].zdot/(slowdown*1000);

    mParticles[i].xdot += mParticles[i].xg;
    mParticles[i].ydot += mParticles[i].yg;
    mParticles[i].zdot += mParticles[i].zg;

    mParticles[i].life -= mParticles[i].fadeSpeed;
    if (mParticles[i].life < 0)
    {
      mParticles[i].life = 1;
      mParticles[i].fadeSpeed = rand()%100/1000. + 0.003;
      mParticles[i].xdot = (rand()%50 - 26.0)*10;
      mParticles[i].ydot = (rand()%50 - 26.0)*10;
      mParticles[i].zdot = (rand()%50 - 26.0)*10;
    }
  }

  //calculate fps
  ++mFramesPainted;
  if (mTime.elapsed() > 1000)
  {
    emit fps(mFramesPainted * 1000 / mTime.restart());
    mFramesPainted = 0;
  }
}

void GLWidget::keyPressEvent(QKeyEvent *e)
{
  switch (e->key())
  {
  case Qt::Key_Left:
    mYRot -= 0.5;
    break;
  case Qt::Key_Right:
    mYRot += 0.5;
    break;
  case Qt::Key_Up:
    mXRot -= 0.5;
    break;
  case Qt::Key_Down:
    mXRot += 0.5;
    break;
  default:
    QGLWidget::keyPressEvent(e);
  }
}

void GLWidget::loadGLTexture(const QString &filename, GLuint textures[3])
{
  //load image
  QImage temp(filename);
  if (temp.isNull())
  {
    temp = QImage(16, 16, QImage::Format_RGB32);
    temp.fill(Qt::green);
    qDebug() << "Can't find image";
  }
  QImage glformatted(QGLWidget::convertToGLFormat(temp)); //convert to gl format

  glGenTextures(1, &textures[0]); //create 1 texture

  glBindTexture(GL_TEXTURE_2D, textures[0]); //create 2d texture at ret
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //filter when image is smaller than texture: good filter
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //filter when image is larger than texture: good filter
  glTexImage2D(GL_TEXTURE_2D, //create 2d texture
               0, //level of detail
               3, //number of data components (rgb = 3)
               glformatted.width(), glformatted.height(), //size of texture
               0, //border
               GL_RGBA, //use rgb in this order
               GL_UNSIGNED_BYTE, //image data is unsigned bytes
               glformatted.bits()); //pointer to image data
}
